using OhioState.Graphics;
using Tao.OpenGl;

namespace OhioState.Graphics.OpenGL
{   
    /// <summary>
    /// A simple sphere approximation that knows how to render itself using OpenGL
    /// </summary>
    public class SphereGL : IDrawable
    {
        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public SphereGL()
            : this(1.0f)
        {
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="radius">The radius of the sphere in world-space</param>
        public SphereGL(float radius)
            : this(radius, 300, false)
        {
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="radius">The radius of the sphere in world-space</param>
        public SphereGL(float radius, int numberOfLongitudeSlices, bool wireframe)
        {
            this.radius = radius;
            this.NumberOfLongitudeSlices = numberOfLongitudeSlices;
            this.Wireframe = wireframe;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Get or set the number of subdivision along the equator.
        /// </summary>
        public int NumberOfLongitudeSlices { get; set; }

        /// <summary>
        /// Get or set whether the sphere should be displayed as a wireframe model.
        /// </summary>
        public bool Wireframe { get; set; }
        #endregion

        #region IDrawable
        /// <summary>
        /// Render the object to the specified render panel.
        /// </summary>
        /// <param name="panel">The render panel that object 
        /// should be rendered to.</param>
        public void Render(IRenderPanel panel)
        {
            material.MakeActive(panel);
            RenderSphere();
            material.Deactivate(panel);
        }

        #endregion

        #region Public Properties
        /// <summary>
        /// Get or set object's material.
        /// </summary>
        public IMaterial Material
        {
            get { return material; }
            set { material = value; }
        }
        /// <summary>
        /// Get the bounding volume.
        /// </summary>        
        public IBoundingVolume BoundingVolume
        {
            get { return null; }
        }
        #endregion

        #region Implementation
        private void RenderSphere()
        {
            if (!created)
            {
                // Create a new display list.
                created = true;
                displayList = Gl.glGenLists(1);
                Gl.glNewList(displayList, Gl.GL_COMPILE);
                // use the glu quadric functions to create the sphere.
                Glu.GLUquadric q;
                q = Glu.gluNewQuadric();
                if( this.Wireframe )
                    Glu.gluQuadricDrawStyle(q, Glu.GLU_LINE);
                else
                    Glu.gluQuadricDrawStyle(q, Glu.GLU_FILL);

                Glu.gluQuadricNormals(q, Glu.GLU_SMOOTH);
                Glu.gluQuadricTexture(q, Glu.GLU_TRUE);
                Glu.gluSphere(q, radius, this.NumberOfLongitudeSlices, this.NumberOfLongitudeSlices/2);
                Gl.glEndList();
            }
            // Once created, we can just call its display list.
            Gl.glCallList(displayList);            
        }
        #endregion

        #region Member variables
        private float radius;
        private IMaterial material;
        private bool created = false;
        private int displayList = 0;
        #endregion
    }
}
